#include <rtthread.h>

#define DBG_TAG "bq769x0"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "bq769x0_drv_i2c.h"
#include "drv_i2c.h"

#include <math.h>

// 报警回调接口
static BQ769X0_AlertOpsTypeDef AlertOps;

//寄存器组定义
static RegisterGroup Registers = {0};

struct rt_i2c_bus_device *i2c_bus = RT_NULL;

//adc的增益
static float Gain = 0;
static int16_t iGain = 0;
static int8_t Adcoffset;

//数据采样的全局变量
BQ769X0_SampleDataTypeDef   BQ769X0_SampleData;


// 热敏电阻阻值换算成温度
static float TempChange(float	Rt)
{
	float temp = 0;

	// 热敏电阻在T2常温下的标称阻值,我买的是10K
	float Rp = 10000;

	// 该热敏电阻在开尔文温度下的,热敏电阻阻值为10K时对应的温度为25度
	float T2 = 273.15 + 25;

	// B值:3935、3950
	float Bx = 3950;

	// 开尔文温度值
	float Ka = 273.15;

	// 打印出热敏电阻的实时阻值可与购买链接的阻值与温度对应表对照查看
	//sprintf((char *)buffer, "%f", Rt);
	//BQ769X0_INFO("Rts value:%s", buffer);

	temp = 1 / (1 / T2 + log(Rt / Rp) / Bx)- Ka + 0.5;

	return temp;
}

//CRC校验、从TI示例中移植
unsigned char CRC8(unsigned char *ptr, unsigned char len,unsigned char key)
{
    unsigned char i;
    unsigned char crc=0;
    while(len--!=0)
    {
        for(i=0x80; i!=0; i/=2)
        {
            if((crc & 0x80) != 0)
            {
                crc *= 2;
                crc ^= key;
            }
            else
                crc *= 2;

            if((*ptr & i)!=0)
                crc ^= key;
        }
        ptr++;
    }
    return(crc);
}

/************************BQ769X0的IIC读写（TI移植）***********************************/
static bool BQ769X0_WriteRegisterByte(uint8_t Register, uint8_t data)
{
    uint8_t dataBuffer[2] = {Register, data};

	struct I2C_MessageTypeDef msg = {0};

	msg.addr = BQ769X0_I2C_ADDR;
	msg.flags = I2C_WR;	
	msg.buf = dataBuffer;
	msg.tLen = 2;
	
    if (I2C_TransferMessages(&i2c1, &msg, 1) != 1)
    {
		LOG_E("Write Register Byte Fail");

		return false;
    }

    return true;
}

static bool BQ769X0_WriteRegisterByteWithCRC(uint8_t Register, uint8_t data)
{
    uint8_t dataBuffer[4];
	struct I2C_MessageTypeDef msg = {0};

	dataBuffer[0] = BQ769X0_I2C_ADDR << 1;
	dataBuffer[1] = Register;
	dataBuffer[2] = data;	
	dataBuffer[3] = CRC8(dataBuffer, 3, CRC_KEY);

	msg.addr = BQ769X0_I2C_ADDR;
	msg.flags = I2C_WR;	
	msg.buf = dataBuffer + 1;
	msg.tLen = 3;

    if (I2C_TransferMessages(&i2c1, &msg, 1) != 1)
    {
		LOG_E("Write Register Byte With CRC Fail");

		return false;
    }

    return true;
}


static bool BQ769X0_WriteRegisterWordWithCRC(uint8_t Register, uint16_t data)
{
    uint8_t dataBuffer[6];
	struct I2C_MessageTypeDef msg = {0};

	dataBuffer[0] = BQ769X0_I2C_ADDR << 1;
	dataBuffer[1] = Register;
	dataBuffer[2] = LOW_BYTE(data);	
	dataBuffer[3] = CRC8(dataBuffer, 3, CRC_KEY);
	dataBuffer[4] = HIGH_BYTE(data);	
	dataBuffer[5] = CRC8(dataBuffer + 4, 1, CRC_KEY);		

	msg.addr = BQ769X0_I2C_ADDR;
	msg.flags = I2C_WR;	
	msg.buf = dataBuffer + 1;
	msg.tLen = 5;

    if (I2C_TransferMessages(&i2c1, &msg, 1) != 1)
    {
		LOG_E("Write Register Word With CRC Fail");
		return false;
    }

    return true;
}


static bool BQ769X0_WriteBlockWithCRC(uint8_t startAddress, uint8_t *buffer, uint8_t length)
{
	uint8_t index;
	uint8_t bufferCRC[32] = {0}, *pointer;
	struct I2C_MessageTypeDef msg = {0};

	pointer = bufferCRC;
	*pointer++ = BQ769X0_I2C_ADDR << 1;
	*pointer++ = startAddress;
	*pointer++ = *buffer;
	*pointer = CRC8(bufferCRC, 3, CRC_KEY);

	for(index = 1; index < length; index++)
	{
        pointer++;
        buffer++;
        *pointer = *buffer;
		*(pointer + 1) = CRC8(pointer, 1, CRC_KEY);
		pointer++;
	}

	msg.addr = BQ769X0_I2C_ADDR;
	msg.flags = I2C_WR;	
	msg.buf = bufferCRC + 1;
	msg.tLen = 2 * length + 1;

    if (I2C_TransferMessages(&i2c1, &msg, 1) != 1)
    {
		LOG_E("Write Register Block With CRC Fail");
		return false;
    }

    return true;	
}



static bool BQ769X0_ReadRegisterByte(uint8_t Register, uint8_t *data)
{  	
	struct I2C_MessageTypeDef msg[2] = {0};

	msg[0].addr = BQ769X0_I2C_ADDR;
	msg[0].flags = I2C_WR;	
	msg[0].buf = &Register;
	msg[0].tLen = 1;

	msg[1].addr = BQ769X0_I2C_ADDR;
	msg[1].flags = I2C_RD;	
	msg[1].buf = data;
	msg[1].tLen = 1;

    if (I2C_TransferMessages(&i2c1, msg, 2) != 2)
    {
		LOG_E("Read Register Byte Fail");
		return false;
    }

    return true;
}

static bool BQ769X0_ReadRegisterByteWithCRC(uint8_t Register, uint8_t *data)
{  	
	uint8_t readBuffer[2], crcInput[2], crcValue;
	struct I2C_MessageTypeDef msg[2] = {0};

	msg[0].addr = BQ769X0_I2C_ADDR;
	msg[0].flags = I2C_WR;	
	msg[0].buf = &Register;
	msg[0].tLen = 1;

	msg[1].addr = BQ769X0_I2C_ADDR;
	msg[1].flags = I2C_RD;	
	msg[1].buf = readBuffer;
	msg[1].tLen = 2;

    if (I2C_TransferMessages(&i2c1, msg, 2) != 2)
    {
		LOG_E("Read Register Byte With CRC Fail");

		return false;
    }


	crcInput[0] = (BQ769X0_I2C_ADDR << 1) + 1;
	crcInput[1] = readBuffer[0];

	crcValue = CRC8(crcInput, 2, CRC_KEY);
	if (crcValue != readBuffer[1])
	{
		LOG_E("Read Register Byte CRC Check Fail");
		return false;
    }

	*data = readBuffer[0];

	return true;
}



static bool BQ769X0_ReadRegisterWordWithCRC(uint8_t Register, uint16_t *data)
{  	
	uint8_t readBuffer[4], crcInput[2], crcValue;
	struct I2C_MessageTypeDef msg[2] = {0};

	msg[0].addr = BQ769X0_I2C_ADDR;
	msg[0].flags = I2C_WR;	
	msg[0].buf = &Register;
	msg[0].tLen = 1;

	msg[1].addr = BQ769X0_I2C_ADDR;
	msg[1].flags = I2C_RD;	
	msg[1].buf = readBuffer;
	msg[1].tLen = 4;

    if (I2C_TransferMessages(&i2c1, msg, 2) != 2)
    {
		LOG_E("Read Register Word With CRC Fail");

		return false;
    }

	crcInput[0] = (BQ769X0_I2C_ADDR << 1) + 1;
	crcInput[1] = readBuffer[0];

	crcValue = CRC8(crcInput, 2, CRC_KEY);
	if (crcValue != readBuffer[1])
	{
		LOG_E("Read Register Word CRC 1 Check Fail");

		return false;
	}

	crcValue = CRC8(readBuffer + 2, 1, CRC_KEY);
	if (crcValue != readBuffer[3])
	{
		LOG_E("Read Register Word CRC 2 Check Fail");

		return false;
	}
	*data = (readBuffer[2] << 8) | readBuffer[0];

	return true;
}

static bool BQ769X0_ReadBlockWithCRC(uint8_t Register, uint8_t *buffer, uint8_t length)
{  	
	uint8_t index, crcValue, crcInput[2];
	uint8_t buf[32] = {0};
	uint8_t *readData = buf;
	struct I2C_MessageTypeDef msg[2] = {0};


	msg[0].addr = BQ769X0_I2C_ADDR;
	msg[0].flags = I2C_WR;	
	msg[0].buf = &Register;
	msg[0].tLen = 1;

	msg[1].addr = BQ769X0_I2C_ADDR;
	msg[1].flags = I2C_RD;	
	msg[1].buf = readData;
	msg[1].tLen = length * 2;

    if (I2C_TransferMessages(&i2c1, msg, 2) != 2)
    {
		LOG_E("Read Register Block With CRC Cail");
		
		return false;
    }

	crcInput[0] = (BQ769X0_I2C_ADDR << 1) + 1;
	crcInput[1] = readData[0];

	crcValue = CRC8(crcInput, 2, CRC_KEY);
	readData++;
	if (crcValue != *readData)
	{
		LOG_E("Read Register Block CRC 1 Check Fail");
		
		return false;	
	}
	else
	{
		*buffer = *(readData - 1);
	}

	for(index = 1; index < length; index++)
	{
		readData++;
		crcValue = CRC8(readData, 1, CRC_KEY);
		readData++;
		buffer++;

		if (crcValue != *readData)
		{
			LOG_E("Read Register Block CRC Check Fail");
			
			return false;		
		}
		else
		{
			*buffer = *(readData - 1);
		}
	}

	return true;
}


/************************end of BQ769X0的IIC读写（TI移植）***********************************/

/************************BQ769X0的传感器数据采集**********************************************/

//单体电芯电压采样
void BQ769X0_UpdateCellVolt(void)
{
    uint32_t    index = 0;
    uint32_t    itemp = 0;
    uint8_t     *pRawADCData = NULL;
    uint32_t    ltemp = 0;

    if (BQ769X0_ReadBlockWithCRC(VC1_HI_BYTE, &(Registers.VCell1.VCell1Byte.VC1_HI), (BQ769X0_CELL_MAX<<1)) != true)
    {
        LOG_E("Update Cell Voltage Fail!\r\n");
    }

    pRawADCData = &Registers.VCell1.VCell1Byte.VC1_HI;

    for (index=0; index<BQ769X0_CELL_MAX; index++)
    {
        itemp = (uint32_t)((*pRawADCData << 8) + *(pRawADCData + 1));
        ltemp = ((unsigned long)itemp * iGain / 1000);
        ltemp += Adcoffset;
        BQ769X0_SampleData.CellVoltage[index] = ltemp/1000.0;
        pRawADCData += 2;
    }
}

//电路电流采集
void BQ769X0_UpdateCurrent(void)
{
    int32_t temp;

    if (BQ769X0_ReadRegisterWordWithCRC(CC_HI_BYTE, &(Registers.CC.CCWord)) != true)
    {
        LOG_E("Update Battery Current Fail!\r\n");
    }

    temp = (Registers.CC.CCByte.CC_HI<<8) | Registers.CC.CCByte.CC_LO;

    if (temp & 0x8000)
    {
        temp = - ((~temp + 1) & 0xffff);
    }
    BQ769X0_SampleData.BatteryCurrent = ((temp * 8.44)/0.005) * 0.000001;
}

//电池总电压采集
void BQ769X0_UpdateBatvolt(void)
{
	uint16_t value;
	
	if (BQ769X0_ReadRegisterWordWithCRC(BAT_HI_BYTE, &(Registers.VBat.VBatWord)) != true)
	{
		LOG_E("Update Batvolt fail!\r\n");
	}
	
	value = (Registers.VBat.VBatByte.BAT_HI << 8) | Registers.VBat.VBatByte.BAT_LO;
	
	BQ769X0_SampleData.BatteryVolatage = 4 * Gain * value;
	BQ769X0_SampleData.BatteryVolatage += BQ769X0_CELL_MAX * Adcoffset;
	BQ769X0_SampleData.BatteryVolatage /= 1000;
}

//热敏电阻温度采集
void BQ769X0_UpdateTstemp(void)
{
	int16_t value;
	uint8_t *pRawADCData = NULL;
	float v_tsx = 0;
	float Rts = 0;
	
//	if (BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x18) != true)
//	{
//		LOG_E("Update temperature fail!\r\n");
//	}
//	
//	rt_thread_mdelay(2000);
	
	
	if (BQ769X0_ReadRegisterWordWithCRC(TS1_HI_BYTE, &(Registers.TS1.TS1Word)) != true)
	{
		LOG_E("Update temperature fail!\r\n");
	}
	
	pRawADCData = &(Registers.TS1.TS1Byte.TS1_HI);
	value = (uint16_t)((*pRawADCData << 8) | *(pRawADCData +1));
	
	v_tsx = value * 0.000382;
	Rts = (10000 * v_tsx) / (3.3 - v_tsx);
	
	BQ769X0_SampleData.TsxTemperature = TempChange(Rts);	
}

/************************end of BQ769X0的传感器数据采集****************************************/

/***************************BQ769X0芯片控制***********************/

// 控制充放电开关
void BQ769X0_ControlDSGOrCHG(BQ769X0_ControlTypeDef ControlType, BQ769X0_StateTypeDef NewState)
{
	if (NewState == BQ_STATE_ENABLE)
	{
		Registers.SysCtrl2.SysCtrl2Byte |= ControlType;
	}
	else
	{
		Registers.SysCtrl2.SysCtrl2Byte &= ~ControlType;
	}
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL2, Registers.SysCtrl2.SysCtrl2Byte);
}

// 设置某个电芯均衡状态，可以位与多节，支持BQ769X0系列(相邻单元不能同时均衡)
void BQ769X0_CellBalanceControl(BQ769X0_CellIndexTypeDef CellIndex, BQ769X0_StateTypeDef NewState)
{
	static uint8_t CELL_BAL_VALUE[3] = {0};

	if (NewState == BQ_STATE_ENABLE)
	{
		CELL_BAL_VALUE[0] |= CellIndex & 0x1F;
		CELL_BAL_VALUE[1] |= (CellIndex >> 5) & 0x1F;
		CELL_BAL_VALUE[2] |= (CellIndex >> 10) & 0x1F;
	}
	else if (NewState == BQ_STATE_DISABLE)
	{
		CELL_BAL_VALUE[0] &= ~(CellIndex & 0x1F);
		CELL_BAL_VALUE[1] &= ~((CellIndex >> 5) & 0x1F);
		CELL_BAL_VALUE[2] &= ~((CellIndex >> 10) & 0x1F);
	}
	BQ769X0_WriteBlockWithCRC(CELLBAL1, CELL_BAL_VALUE, 3);
}
/***************************end of BQ769X0芯片控制*********************/


/**************************BQ769X0报警*******************************/

// 报警处理
static void BQ769X0_AlertyHandler(void)
{
	uint8_t reg_value = 0, write_value = 0;

	BQ769X0_ReadRegisterByteWithCRC(SYS_STAT, &reg_value);
	if (reg_value & SYS_STAT_OCD_BIT)
	{
		write_value |= SYS_STAT_OCD_BIT;
		if (AlertOps.ocd != NULL) AlertOps.ocd();
	}

	if (reg_value & SYS_STAT_SCD_BIT)
	{
		write_value |= SYS_STAT_SCD_BIT;
		if (AlertOps.scd != NULL) AlertOps.scd();
	}

	if (reg_value & SYS_STAT_OV_BIT)
	{
		write_value |= SYS_STAT_OV_BIT;
		if (AlertOps.ov != NULL) AlertOps.ov();
	}	

	if (reg_value & SYS_STAT_UV_BIT)
	{
		write_value |= SYS_STAT_UV_BIT;
		if (AlertOps.uv != NULL) AlertOps.uv();
	}

	if (reg_value & SYS_STAT_OVRD_BIT)
	{
		write_value |= SYS_STAT_OVRD_BIT;
		if (AlertOps.ovrd != NULL) AlertOps.ovrd();
	}	

	if (reg_value & SYS_STAT_DEVICE_BIT)
	{
		write_value |= SYS_STAT_DEVICE_BIT;
		if (AlertOps.device != NULL) AlertOps.device();
	}
	
	if (reg_value & SYS_STAT_CC_BIT)
	{
		write_value |= SYS_STAT_CC_BIT;
		if (AlertOps.cc != NULL) AlertOps.cc();
	}		

	BQ769X0_WriteRegisterByteWithCRC(SYS_STAT, write_value);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_12)
    {
		BQ769X0_AlertyHandler();
    }
}


/***************************end of BQ769X0报警*********************/

/*************************BQ769X0初始化函数***************************************************/
static void BQ769X0_TS1_SetOutMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

static void BQ769X0_TS1_SetInMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
	
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 唤醒BQ芯片
void BQ769X0_Wakeup(void)
{
    // 输出模式，推挽输出高电平唤醒BQ芯片
    BQ769X0_TS1_SetOutMode();
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
    rt_thread_mdelay(1000);

    // 设为输入模式，避免干扰温度采样
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
    BQ769X0_TS1_SetInMode();
    rt_thread_mdelay(1000);
}
void BQ769X0_EntryShip(void)
{
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x00);
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x01);
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x02);
}



void BQ769X0_GetADCGainOffset(void)
{
    BQ769X0_ReadRegisterByteWithCRC(ADCGAIN1, &(Registers.ADCGain1.ADCGain1Byte));
    BQ769X0_ReadRegisterByteWithCRC(ADCGAIN2, &(Registers.ADCGain2.ADCGain2Byte));
    BQ769X0_ReadRegisterByteWithCRC(ADCOFFSET, &(Registers.ADCOffset));

    /*GAIN is uV/LSB,OFFSET is mV*/
    Gain = (ADCGAIN_BASE + ((Registers.ADCGain1.ADCGain1Byte & 0x0C) << 1) + ((Registers.ADCGain2.ADCGain2Byte & 0xE0)>> 5)) / 1000.0;
    iGain = ADCGAIN_BASE + ((Registers.ADCGain1.ADCGain1Byte & 0x0C) << 1) + ((Registers.ADCGain2.ADCGain2Byte & 0xE0)>> 5);

    if (Registers.ADCOffset <= 0x7F)
    {
        Adcoffset = Registers.ADCOffset;    // 正数，直接返回
    }
    else
    {
        Adcoffset = Registers.ADCOffset - 256;  // 负数，手动处理
    }

}

static void BQ769X0_Configuration(void)
{
    unsigned char ReadBuffer[8];

    // 开ADC,选择外部NTC
    Registers.SysCtrl1.SysCtrl1Byte = 0x18;

    // 使能电流连续采样，关闭充放电MOS
    Registers.SysCtrl2.SysCtrl2Byte = 0x40;

    // 配置CC_CFG,说明书要求在初始化时应配置为0X19以获得更好的性能
    Registers.CCCfg = 0x19;

    // 写入配置到寄存器
    BQ769X0_WriteBlockWithCRC(SYS_CTRL1, &(Registers.SysCtrl1.SysCtrl1Byte), 8);
    BQ769X0_ReadBlockWithCRC(SYS_CTRL1, ReadBuffer, 8);


    // 去掉BUFF[0]的最高位,防止因为接上了负载使负载检测置位而没通过校验
    if( (ReadBuffer[0]&0X7F) != Registers.SysCtrl1.SysCtrl1Byte
    || ReadBuffer[1] != Registers.SysCtrl2.SysCtrl2Byte
    || ReadBuffer[2] != Registers.Protect1.Protect1Byte
    || ReadBuffer[3] != Registers.Protect2.Protect2Byte
    || ReadBuffer[4] != Registers.Protect3.Protect3Byte
    || ReadBuffer[5] != Registers.OVTrip
    || ReadBuffer[6] != Registers.UVTrip
    || ReadBuffer[7] != Registers.CCCfg)
    {
        LOG_E("BQ769X0 config register fail,Please reset BMS board");

        while(1);
    }
}

void BQ769X0_Initialize(BQ769X0_InitDataTypeDef* InitData_t)
{
	// 进入睡眠再唤醒相当于复位一次BQ芯片
	BQ769X0_EntryShip();
	rt_thread_mdelay(500);
	BQ769X0_Wakeup();
	
    // 获取增益和偏移量
    BQ769X0_GetADCGainOffset();


    //AlertOps = InitData->AlertOps;


    // 配置寄存器
    Registers.Protect1.Protect1Bit.SCD_DELAY = InitData_t->ConfigData.SCDDelay;
    Registers.Protect1.Protect1Bit.SCD_THRESH = InitData_t->ConfigData.SCDThresh;
    Registers.Protect2.Protect2Bit.OCD_DELAY = InitData_t->ConfigData.OCDDelay;
    Registers.Protect2.Protect2Bit.OCD_THRESH = InitData_t->ConfigData.OVThresh;
    Registers.Protect3.Protect3Bit.OV_DELAY = InitData_t->ConfigData.OVDelay;
    Registers.Protect3.Protect3Bit.UV_DELAY = InitData_t->ConfigData.UVDelay;

    // BQ阈值寄存器内部比较是14位的，但我们真实写入的值是“10-XXXX-XXXX–1000”中间x的数据，所以下面计算出14位数据后需要得到中间8位再写入
    Registers.OVTrip = (uint8_t)((((uint16_t)((InitData_t->ConfigData.OVThresh - Adcoffset)/Gain/* + 0.5*/) - OV_THRESH_BASE) >> 4) & 0xFF);
    // BQ阈值寄存器内部比较是14位的，但我们真实写入的值是“01-XXXX-XXXX–0000”中间x的数据，所以下面计算出14位数据后需要得到中间8位再写入
    Registers.UVTrip = (uint8_t)((((uint16_t)((InitData_t->ConfigData.UVThresh - Adcoffset)/Gain/* + 0.5*/) - UV_THRESH_BASE) >> 4) & 0xFF);


    BQ769X0_Configuration();

    LOG_I("BQ769X0 Initialize successful!");
}


/*************************end of BQ769X0初始化函数**********************************************/












